﻿#region licence
//  Copyright 2008 Michael Cromwell

//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at

//       http://www.apache.org/licenses/LICENSE-2.0

//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Boo.Lang.Compiler;
using Boo.Lang.Compiler.Ast;

namespace log4net.altconf.Macros
{
    /// <summary>
    /// Used for syntatic sugar, redirects assignments and method calls to the 
    /// appender in context.
    /// <example>Target = "Console.Out" becomes currentAppender.Target = "Console.Out"</example>
    /// </summary>
    public class Add_appenderMacro : AbstractAstMacro
    {
        public override Statement Expand(MacroStatement macro)
        {
            if (macro.Arguments.Count != 2)
            {
                CompilerErrorFactory.CustomError(macro.LexicalInfo, "appender must be called with 2 arguments, add_appender @appender_name, AppenderType");
                return null;
            }

            var block = new Block();
            var mie = new MethodInvocationExpression(
                            new MemberReferenceExpression(
                                new SelfLiteralExpression(macro.LexicalInfo),
                                "AddAppender"));
            mie.Arguments.Add(macro.Arguments[0]);
            mie.Arguments.Add(macro.Arguments[1]);
            block.Add(mie);

            new BlockToReferencesTransformer()
                    .Visit(macro.Block);

            block.Add(macro.Block);

            return block;
        }

        internal class BlockToReferencesTransformer : DepthFirstTransformer
        {
            
            public override void OnExpressionStatement(ExpressionStatement node)
            {
                if (AstUtil.IsAssignment(node.Expression))
                    TransformAssignment(node);
                else if (node.Expression is MethodInvocationExpression)
                    TransformMethodCall(node);                                    
                                
                base.OnExpressionStatement(node);
            }

            private static void TransformMethodCall(ExpressionStatement node)
            {
                var orig = node.Expression as MethodInvocationExpression;
                var mthName = (orig.Target as ReferenceExpression).Name;
                var mie = new MethodInvocationExpression(
                                new MemberReferenceExpression(
                                    new ReferenceExpression("currentAppender"),
                                    mthName));
                mie.Arguments = orig.Arguments;
                node.Expression = mie;
            }

            private static void TransformAssignment(ExpressionStatement node)
            {
                var bin = node.Expression as BinaryExpression;
                var lhs = bin.Left.CloneNode() as ReferenceExpression;

                var memRefExpr = new MemberReferenceExpression(
                                        new ReferenceExpression(lhs.LexicalInfo, "currentAppender"),
                                        lhs.Name);

                memRefExpr.LexicalInfo = lhs.LexicalInfo;
                bin.Left = memRefExpr;
            }

            
        }

    }
}
